package zhuzx.generator;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.zhuzx.util.GeneralUtil;
import org.zhuzx.util.MicroUtil;
import org.zhuzx.util.WebUtil;

/**
 * json字符串转pojo源码
 * 遗留问题：
 * 1、字段值本身是json串，未给其中的引号加重，导致出错。
 * 2、标准格式的json，未格式化时（即一行紧凑的文本）如何处置？
 * 可以考虑使用正则统一给所有字符串值加重引号，目前的加重依据是单行以内寻找第三个引号，去除单行限制考虑这个问题。
 * @author zhuzx 2019年05月24日
 */
public class JsonToClassUtil {
	
	private static boolean allowMakeStringValueAsComment;
	static final String testJson =
			"\r\n{" + 
			"\r\n    \"business_type\":\"000\",//业务类型" + 
			"\r\n    \"is_updated\":true, //pdf文档是否更新版本" + 
			"\r\n    \"tableDatas\":[" + 
			"\r\n        {" + 
			"\r\n            \"unique\":{ //判断唯一性的条件" + 
			"\r\n                \"com_uni_code\":\"xxxxx\"," + 
			"\r\n                \"end_date\":\"xxxxx\"," + 
			"\r\n                \"remark\":\"0\"" + 
			"\r\n            }," + 
			"\r\n            \"db\":{//数据库连接信息" + 
			"\r\n                \"dbName\":\"data_center_zsc\"," + 
			"\r\n                \"dbType\":\"MySQL\",//指明用的何种数据库" + 
			"\r\n                \"host\":\"10.252.218.51\"," + 
			"\r\n                \"password\":\"D6oEXXYI11B5\"," + 
			"\r\n                \"port\":3306," + 
			"\r\n                \"userName\":\"dc_spring_rw_te\"" + 
			"\r\n            }," + 
			"\r\n            \"tableName\":\"xxxx\"" + 
			"\r\n        }" + 
			"\r\n    ]," + 
			"\r\n    \"attach\":{" + 
			"\r\n        \"srcId\":\"xxxxx\"," + 
			"\r\n        \"modelId\":\"xxxxx\"//模板id" + 
			"\r\n    }" + 
			"\r\n}";
	
	
	public static void main(String[] args) {
		
//		GuiUtil.sendToClipboard(testJson);
		
		String json = GuiUtil.getTextFromClipboard();
		if (GeneralUtil.isBlank(json)) {
			System.out.println("剪贴板中没有文本内容，结束。");
			return;
		}
		System.out.println(json);
		allowMakeStringValueAsComment = GuiUtil.confirm("当字符串类型的字段没有注释时，是否使用字段值作为注释？");
		String specJson = preprocess(json);
		System.out.println("\n" + specJson);
		String srcCode = createClassCode(specJson, "AnClass");
		if (srcCode.contains("List<")) {
			srcCode = "import java.util.List;\n\n" + srcCode;
		}
		System.out.println("\n" + srcCode);
//		GuiUtil.sendToClipboard(srcCode);
	}
	
	private static String preprocess(String json) {
		json = json.trim();
		if (json.indexOf('\n') == -1) {
			System.out.println("传入的是不带注释的标准json串，按普通流程处理。");
			return json;
		}
		json = json.replace('', ' ');//这个符号无法被正则的\s匹配到，比如"".matches("\\s*")会输出false。
		StringBuilder sb = new StringBuilder();
		for (String line : json.split("\n")) {
			int commentIndex = MicroUtil.findFirst(line, "(?<!http(s)?:)//");
			if (commentIndex > -1) {//将注释前移，合并为键名的一部分。
				int keyEndIndex = line.indexOf('"', line.indexOf('"') + 1);
				String comment = line.substring(commentIndex).trim().replace('"', '\'');
				line = line.substring(0, keyEndIndex).trim() + comment
						+ line.substring(keyEndIndex, commentIndex).trim();
			} else {
				line = line.trim();
			}
			//将字符串类型的值再加一层双引号
			String stringTypeKey = MicroUtil.extractFirst(line, "\"[^\"]+\"\\s*:\\s*\"");
			if (stringTypeKey != null) {
				String value = line.substring(stringTypeKey.length());
				if (value.charAt(value.length()-1) == ',') {//尾部的逗号必须在两层引号的外面
					value = value.substring(0, value.length()-1) + "\",";
				} else {
					value += "\"";
				}
				line = stringTypeKey + "\"" + value;
			}
			sb.append(line);
		}
		return sb.toString();
	}
	
	private static String createClassCode(String specJson, String className) {
		Map<String,String> map = WebUtil.createMapFromJson(specJson);
		List<Field> fields = new ArrayList<>(map.size());
		List<String> classCodeList = new ArrayList<>();
		for (Map.Entry<String,String> entry : map.entrySet()) {
			String name = entry.getKey();
			String value = entry.getValue();
			String comment = null;
			int commIndex = MicroUtil.findFirst(name, "(?<!http(s)?:)//");
			if (commIndex > -1) {
				comment = name.substring(commIndex + 2);
				name = name.substring(0, commIndex);
			}
			int oldSize = classCodeList.size();
			String type = getType(name, value, classCodeList);
			if ("String".equals(type)) {
				value = value.substring(1, value.length()-1);//去掉preprocess加的引号
				if (GeneralUtil.isBlank(comment) && allowMakeStringValueAsComment) {
					comment = value;
				}
			}
			if (classCodeList.size() > oldSize && !GeneralUtil.isBlank(comment)) {
				int lastIndex = classCodeList.size() - 1;
				String classComment = "/**\n * " + comment + "\n */\n";
				classCodeList.set(lastIndex, classComment + classCodeList.get(lastIndex));
			}
			Field field = new Field(name, type, comment);
			fields.add(field);
		}
		StringBuilder src = ClassSourceCodeUtil.createSimpleCode(createFieldArray(fields), className);
		if (classCodeList.size() > 0) {
			src.deleteCharAt(src.length() - 1).append('\n');
			for (String classCode : classCodeList) {
				classCode = '\n' + classCode.replace("public class ", "private static class ");
				src.append(classCode.replace("\n", "\n\t")).append('\n');
			}
			src.append('}');
		}
		return src.toString();
	}
	
	private static String[][] createFieldArray(List<Field> fields) {
		String[][] result = new String[fields.size()][];
		for (int i = 0; i < result.length; i++) {
			Field field = fields.get(i);
			String[] array = new String[4];
			array[0] = field.name;
			array[1] = field.type;
			array[3] = field.comment;
			result[i] = array;
		}
		return result;
	}
	
	private static String getType(String key, String value, List<String> classCodeList) {
		if (value == null) {
			return "Object";
		}
		String type;
		char firstChar = value.charAt(0);
		if (firstChar == '"') {
			type = "String";
		} else if (value.equals("true") || value.equals("false")) {
			type = "boolean";
		} else if (value.matches("^-?\\d+$")) {
			if (value.length() <= 5) {//此处有风险，待定。
				type = "int";
			} else {
				type = "long";
			}
		} else if (value.matches("^-?\\d+\\.\\d+$")) {
			type = "double";
		} else if (firstChar == '{') {
			type = MicroUtil.capitalizeTheFirstChar(key);
			classCodeList.add(createClassCode(value, type));
		} else if (firstChar == '[') {
			type = getTypeOfList(key, value, classCodeList);
		} else {
			throw new RuntimeException("未知的值类型：\nkey：" + key + "\nvalue：" + value);
		}
		return type;
	}
	
	private static String getTypeOfList(String key, String value, List<String> classCodeList) {
		int hierarchy = 0;
		while (!GeneralUtil.isBlank(value) && value.charAt(0) == '[') {
			value = value.substring(1, value.length()-1).trim();
			hierarchy++;
		}
		if (GeneralUtil.isBlank(value)) {
			return "Object";
		} else if (value.charAt(0) != '{') {
			//若是字符串类型，即使值里面含有逗号也不影响类型判定，非字符串类型的值里面不会有逗号。
			int index = value.indexOf(',');
			if (index > -1) {
				value = value.substring(0, index).trim();
			}
		}
		String type = getType(key, value, classCodeList);
		if (type.equals("int")) {
			type = "Integer";
		} else {
			type = MicroUtil.capitalizeTheFirstChar(type);
		}
		for (; hierarchy > 0; hierarchy--) {
			type = "List<" + type + ">";
		}
		return type;
	}
	
	private static class Field {
		String name;
		String type;
		String comment;
		public Field(String name, String type, String comment) {
			super();
			this.name = name;
			this.type = type;
			this.comment = comment;
		}
	}
	
}